home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 …ember: Reference Library / Dev.CD Dec 00 RL Disk 1.toast / pc / technical documentation / develop / develop issue 26 / develop issue 26 code / qd3d custom attributes / customattribute / customattributessupport.c < prev    next >
Encoding:
Text File  |  1996-03-21  |  27.9 KB  |  951 lines

  1. // QuickDraw 3d Sample Code
  2. //
  3. // This file contains utility routines for QuickDraw 3d sample code.
  4. // Shows how to read a metafile and render it.
  5. //
  6. // Created 27th Dec 1994, Nick Thompson, DEVSUPPORT
  7.  
  8.  
  9. #include <Files.h>
  10. #include <QuickDraw.h>
  11. #include <QDOffScreen.h>
  12. #include <StandardFile.h>
  13. #include <TextUtils.h>
  14. #include <Strings.h>
  15.  
  16. #include "CustomAttributesSupport.h"
  17. #include "CustomAttributesShell.h"
  18.  
  19. #include <QD3D.h>
  20. #include <QD3DDrawContext.h>
  21. #include <QD3DRenderer.h>
  22. #include <QD3DShader.h>
  23. #include <QD3DCamera.h>
  24. #include <QD3DLight.h>
  25. #include <QD3DGeometry.h>
  26. #include <QD3DTransform.h>
  27. #include <QD3DGroup.h>
  28. #include <QD3DMath.h>
  29.  
  30. #include <QD3DStorage.h>
  31. #include <QD3DIO.h>
  32. #include <QD3DString.h>
  33.  
  34. #include "CustomAttribute_Lib.h"
  35.  
  36. //-----------------------------------------------------------------------------------------------
  37. // local utility functions
  38. static    TQ3FileObject         MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText ) ;
  39.  
  40. void GetGroupBBox(
  41.     DocumentPtr            theDocument,
  42.     TQ3BoundingBox         *viewBBox) ;
  43.                                                 
  44. static    TQ3Status MyAddShaderToGroup( TQ3GroupObject group ) ;
  45.  
  46. static TQ3Status GetDocumentGroupBoundingBox( 
  47.     DocumentPtr theDocument , 
  48.     TQ3BoundingBox *viewBBox) ;
  49.  
  50. //-----------------------------------------------------------------------------------------------
  51. // Submit the scene for rendering/fileIO and picking
  52. TQ3Status SubmitScene( DocumentPtr theDocument ) 
  53. {        
  54.     Q3Style_Submit(theDocument->fInterpolation, theDocument->fView);
  55.     Q3Style_Submit(theDocument->fBackFacing , theDocument->fView);
  56.     Q3Style_Submit(theDocument->fFillStyle, theDocument->fView);
  57.         
  58.     Q3MatrixTransform_Submit( &theDocument->fRotation, theDocument->fView);
  59.         
  60.     Q3DisplayGroup_Submit( theDocument->fModel, theDocument->fView);
  61.     
  62.     return kQ3Success ;
  63. }
  64.  
  65. //-----------------------------------------------------------------------------------------------
  66.  
  67. static TQ3Status GetDocumentGroupBoundingBox( 
  68.     DocumentPtr theDocument , 
  69.     TQ3BoundingBox *viewBBox)
  70. {
  71.     TQ3Status        status;
  72.     TQ3ViewStatus    viewStatus ;
  73.     
  74.     status = Q3View_StartBoundingBox( theDocument->fView, kQ3ComputeBoundsApproximate );
  75.     do {
  76.         status = SubmitScene( theDocument ) ;
  77.     } while((viewStatus = Q3View_EndBoundingBox( theDocument->fView, viewBBox )) == kQ3ViewStatusRetraverse );
  78.     return status ;
  79. }
  80.  
  81. //-----------------------------------------------------------------------------------------------
  82.  
  83. TQ3ViewObject MyNewView(WindowPtr theWindow)
  84. {
  85.     TQ3Status                myStatus;
  86.     TQ3ViewObject            myView;
  87.     TQ3DrawContextObject        myDrawContext;
  88.     TQ3RendererObject        myRenderer;
  89.     TQ3CameraObject            myCamera;
  90.     TQ3GroupObject            myLights;
  91.     
  92.     myView = Q3View_New();
  93.     
  94.     //    Create and set draw context.
  95.     if ((myDrawContext = MyNewDrawContext(theWindow)) == nil )
  96.         goto bail;
  97.         
  98.     if ((myStatus = Q3View_SetDrawContext(myView, myDrawContext)) == kQ3Failure )
  99.         goto bail;
  100.  
  101.     Q3Object_Dispose( myDrawContext ) ;
  102.     
  103.     // Create and set renderer.
  104.     //
  105.     // hacky way to do this, but since I wanted these snippets to have 
  106.     // a minimal interface, this will suffice
  107.     //
  108.     // change the next line to “#if 1” to use the WF renderer
  109.     
  110. #if 0
  111.     // this would use the wireframe renderer
  112.     myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeWireFrame);
  113.     if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
  114.         goto bail;
  115.     }
  116. #else
  117.     // this would use the interactive software renderer
  118.  
  119.     if ((myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive)) != nil ) {
  120.         if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
  121.             goto bail;
  122.         }
  123.     }
  124.     else {
  125.         goto bail;
  126.     }
  127. #endif
  128.  
  129.     Q3Object_Dispose( myRenderer ) ;
  130.     
  131.     //    Create and set camera.
  132.     if ( (myCamera = MyNewCamera(theWindow)) == nil )
  133.         goto bail;
  134.         
  135.     if ((myStatus = Q3View_SetCamera(myView, myCamera)) == kQ3Failure )
  136.         goto bail;
  137.  
  138.     Q3Object_Dispose( myCamera ) ;
  139.     
  140.     //    Create and set lights.
  141.     if ((myLights = MyNewLights()) == nil )
  142.         goto bail;
  143.         
  144.     if ((myStatus = Q3View_SetLightGroup(myView, myLights)) == kQ3Failure )
  145.         goto bail;
  146.         
  147.     Q3Object_Dispose(myLights);
  148.  
  149.     //    Done!!!
  150.     return ( myView );
  151.     
  152. bail:
  153.     //    If any of the above failed, then don't return a view.
  154.     return ( nil );
  155. }
  156.  
  157. //----------------------------------------------------------------------------------
  158.  
  159. TQ3DrawContextObject MyNewDrawContext(WindowPtr theWindow)
  160. {
  161.     TQ3DrawContextData        myDrawContextData;
  162.     TQ3MacDrawContextData    myMacDrawContextData;
  163.     TQ3ColorARGB            ClearColor;
  164.     TQ3DrawContextObject    myDrawContext ;
  165.     
  166.     ClearColor.a = 1.0;
  167.     ClearColor.r = 1.0;
  168.     ClearColor.g = 1.0;
  169.     ClearColor.b = 1.0;
  170.     
  171.     //    Fill in draw context data.
  172.     myDrawContextData.clearImageMethod = kQ3ClearMethodWithColor;
  173.     myDrawContextData.clearImageColor = ClearColor;
  174.     
  175.     myDrawContextData.paneState = kQ3False;
  176.     myDrawContextData.maskState = kQ3False;
  177.     
  178.     myDrawContextData.doubleBufferState = kQ3True;
  179.  
  180.     myMacDrawContextData.drawContextData = myDrawContextData;
  181.     
  182.     myMacDrawContextData.window = (CGrafPtr) theWindow;        // this is the window associated with the view
  183.     myMacDrawContextData.library = kQ3Mac2DLibraryNone;
  184.     myMacDrawContextData.viewPort = nil;
  185.     myMacDrawContextData.grafPort = nil;
  186.     
  187.     //    Create draw context and return it, if it’s nil the caller must handle
  188.     myDrawContext = Q3MacDrawContext_New(&myMacDrawContextData) ;
  189.  
  190.     return myDrawContext ;
  191. }
  192.  
  193. //----------------------------------------------------------------------------------
  194.  
  195. TQ3CameraObject MyNewCamera(WindowPtr theWindow)
  196. {
  197.     TQ3CameraObject                    myCamera;
  198.     TQ3CameraData                    myCameraData;
  199.     TQ3ViewAngleAspectCameraData        myViewAngleCameraData;
  200.     TQ3Point3D                        cameraFrom     = { 0.0, 0.0, 1.5 };
  201.     TQ3Point3D                        cameraTo     = { 0.0, 0.0, 0.0 };
  202.     TQ3Vector3D                        cameraUp     = { 0.0, 1.0, 0.0 };
  203.     
  204.     float                             hither         = 0.4;
  205.     float                             yon         = 3.6;
  206.     
  207.     /*
  208.          we are going to set this up so that we have a view volume that measures 2x2x2
  209.          and is centered on 0,0,0.  In this demo, the default units are in meters, so 
  210.          that's a 2 meter box.
  211.     */
  212.     
  213.     //    Fill in camera data.
  214.     myCameraData.placement.cameraLocation = cameraFrom;
  215.     myCameraData.placement.pointOfInterest = cameraTo;
  216.     myCameraData.placement.upVector = cameraUp;
  217.     
  218.     myCameraData.range.hither = hither;
  219.     myCameraData.range.yon = yon;
  220.     
  221.     myCameraData.viewPort.origin.x = -1.0;
  222.     myCameraData.viewPort.origin.y = 1.0;
  223.     myCameraData.viewPort.width = 2.0;
  224.     myCameraData.viewPort.height = 2.0;
  225.     
  226.     myViewAngleCameraData.cameraData = myCameraData;
  227.     
  228.     // the half with is 1 at a distance of 1.5
  229.     myViewAngleCameraData.fov = 2 * atan( 1 / 1.5) ;
  230.     
  231.     // set up the aspect ratio based on the window
  232.     myViewAngleCameraData.aspectRatioXToY =  
  233.             (float) (theWindow->portRect.right - theWindow->portRect.left) / 
  234.             (float) (theWindow->portRect.bottom - theWindow->portRect.top);
  235.  
  236.     myCamera = Q3ViewAngleAspectCamera_New(&myViewAngleCameraData);    
  237.     
  238.     //    Return the camera.
  239.     return ( myCamera );
  240. }
  241.  
  242.  
  243. //----------------------------------------------------------------------------------
  244.  
  245. TQ3GroupObject MyNewLights()
  246. {
  247.     TQ3GroupPosition            myGroupPosition;
  248.     TQ3GroupObject            myLightList;
  249.     TQ3LightData                myLightData;
  250.     TQ3PointLightData        myPointLightData;
  251.     TQ3DirectionalLightData    myDirectionalLightData;
  252.     TQ3LightObject            myAmbientLight, myPointLight, myFillLight;
  253.     TQ3Point3D                pointLocation = { -10.0, 0.0, 10.0 };
  254.     TQ3Vector3D                fillDirection = { 10.0, 0.0, 10.0 };
  255.     TQ3ColorRGB                WhiteLight = { 1.0, 1.0, 1.0 };
  256.     
  257.     //    Set up light data for ambient light.  This light data will be used for point and fill
  258.     //    light also.
  259.  
  260.     myLightData.isOn = kQ3True;
  261.     myLightData.color = WhiteLight;
  262.     
  263.     //    Create ambient light.
  264.     myLightData.brightness = .2;
  265.     myAmbientLight = Q3AmbientLight_New(&myLightData);
  266.     if ( myAmbientLight == nil )
  267.         goto bail;
  268.     
  269.     //    Create point light.
  270.     myLightData.brightness = 1.0;
  271.     myPointLightData.lightData = myLightData;
  272.     myPointLightData.castsShadows = kQ3False;
  273.     myPointLightData.attenuation = kQ3AttenuationTypeNone;
  274.     myPointLightData.location = pointLocation;
  275.     myPointLight = Q3PointLight_New(&myPointLightData);
  276.     if ( myPointLight == nil )
  277.         goto bail;
  278.  
  279.     //    Create fill light.
  280.     myLightData.brightness = .2;
  281.     myDirectionalLightData.lightData = myLightData;
  282.     myDirectionalLightData.castsShadows = kQ3False;
  283.     myDirectionalLightData.direction = fillDirection;
  284.     myFillLight = Q3DirectionalLight_New(&myDirectionalLightData);
  285.     if ( myFillLight == nil )
  286.         goto bail;
  287.  
  288.     //    Create light group and add each of the lights into the group.
  289.     myLightList = Q3LightGroup_New();
  290.     if ( myLightList == nil )
  291.         goto bail;
  292.     myGroupPosition = Q3Group_AddObject(myLightList, myAmbientLight);
  293.     if ( myGroupPosition == 0 )
  294.         goto bail;
  295.     myGroupPosition = Q3Group_AddObject(myLightList, myPointLight);
  296.     if ( myGroupPosition == 0 )
  297.         goto bail;
  298.     myGroupPosition = Q3Group_AddObject(myLightList, myFillLight);
  299.     if ( myGroupPosition == 0 )
  300.         goto bail;
  301.  
  302.     Q3Object_Dispose( myAmbientLight ) ;
  303.     Q3Object_Dispose( myPointLight ) ;
  304.     Q3Object_Dispose( myFillLight ) ;
  305.  
  306.     //    Done!
  307.     return ( myLightList );
  308.     
  309. bail:
  310.     //    If any of the above failed, then return nothing!
  311.     return ( nil );
  312. }
  313.  
  314. //----------------------------------------------------------------------------------
  315.  
  316. TQ3GroupObject MyNewModelFromFile(FSSpec *theFileSpec, DocumentPtr theDocument)
  317. {
  318.     TQ3GroupObject        mainGroup = NULL, modelGroup = NULL;
  319.     TQ3Boolean            isText = kQ3False ;
  320.     TQ3FileMode            myFileMode = 0;
  321.     TQ3FileObject        theFile;
  322.     
  323.     //    Create a ordered group for all the elements that make up the group.
  324.     if ((mainGroup = Q3OrderedDisplayGroup_New()) == NULL )
  325.         return NULL;
  326.         
  327.     MyAddShaderToGroup( mainGroup ) ;
  328.  
  329.     //    Create a display group for the things that we read from the file.
  330.     if ((modelGroup = Q3DisplayGroup_New()) == NULL )
  331.         return NULL;
  332.  
  333.     // Add the model group to the document's group
  334.     
  335.     Q3Group_AddObject(mainGroup, modelGroup);
  336.     
  337.     theFile = MyGetNewFile( theFileSpec, &isText ) ;
  338.     
  339.     if( isText == kQ3True )
  340.         myFileMode |= kQ3FileModeText;    // is it a text metafile??    
  341.  
  342.     // Open the file object
  343.     if( Q3File_OpenRead( theFile, &myFileMode ) != kQ3Success)
  344.         return  NULL ;
  345.  
  346.     theDocument->fModel = mainGroup;
  347.  
  348.     if( MyReadModelFromFile( theFile, modelGroup, theDocument ) == kQ3Failure)
  349.         DebugStr("\pMetafile data read is null") ;
  350.     
  351.     Q3File_Close(theFile);            // close and dispose of the file object
  352.     Q3Object_Dispose(theFile);
  353.     
  354.     
  355.     return mainGroup ;
  356. }
  357.  
  358.  
  359. //----------------------------------------------------------------------------------
  360. // attach a shader to the group
  361.  
  362. TQ3Status MyAddShaderToGroup( TQ3GroupObject group )
  363. {
  364.     TQ3ShaderObject    illuminationShader = Q3PhongIllumination_New();
  365.  
  366.     Q3Group_AddObject(group, illuminationShader);
  367.     Q3Object_Dispose(illuminationShader);
  368.     return(kQ3Success);
  369. }
  370.  
  371. //----------------------------------------------------------------------------------
  372. // read model from file object into the supplied group
  373.  
  374. TQ3Status MyReadModelFromFile( TQ3FileObject theFile,TQ3GroupObject myGroup, DocumentPtr theDocument)
  375. {
  376.     TQ3Boolean    alreadyGotAttributes = kQ3False;
  377.     
  378.     if(theFile != NULL) {
  379.     
  380.         TQ3Object            myTempObj ;
  381.         TQ3Boolean            isEOF ;
  382.                 
  383.     
  384.         // read objects from the file
  385.         do {
  386.         
  387.             myTempObj = Q3File_ReadObject( theFile );
  388.             
  389.             if( myTempObj != NULL ) {
  390.                 // we only want the object in our main group if we can draw it
  391.                 if ( Q3Object_IsDrawable( myTempObj) ) {
  392.                     
  393.                     if( alreadyGotAttributes == kQ3False) {
  394.                         TQ3SetObject        set = NULL, groupSet = NULL;
  395.                         
  396.                         /*
  397.                             It only makes sense to interpret attributes like scale, up vector, and front vector on a file
  398.                             as a whole.  So, once we find one, that's the one we are going to use.
  399.                         */
  400.                         if( Q3Object_IsType(myTempObj, kQ3ShapeTypeGeometry) == kQ3True) {
  401.                             Q3Geometry_GetAttributeSet(myTempObj, &set);
  402.                         }
  403.                         
  404.                         if(set == NULL && Q3Object_IsType(myTempObj, kQ3SharedTypeShape) == kQ3True){
  405.                             Q3Shape_GetSet(myTempObj, &set);
  406.                         }
  407.                         
  408.                         // we are going to migrate the attributes up
  409.                         Q3Shape_GetSet(theDocument->fModel, &groupSet);
  410.                         if( groupSet == NULL) {
  411.                             groupSet = Q3Set_New();
  412.                             if( groupSet ) {
  413.                                 Q3Shape_SetSet(theDocument->fModel, groupSet);
  414.                             }
  415.                         }            
  416.  
  417.                         if( set ) {
  418.                             if( Q3Set_Contains( set, kElementTypeScale) == kQ3True ) {
  419.                                 double    scale;
  420.                                 
  421.                                 Q3Set_Get(set, kElementTypeScale, &scale);
  422.                                 
  423.                                 alreadyGotAttributes = kQ3True;
  424.                                 
  425.                                 Q3Shape_GetSet(theDocument->fModel, &groupSet);
  426.                                 
  427.                                 if( groupSet )
  428.                                     Q3Set_Add(groupSet, kElementTypeScale, &scale);
  429.                             }
  430.                     
  431.                             if( Q3Set_Contains( set, kElementTypeUpVector) == kQ3True ) {
  432.                                 TQ3Vector3D    upVector;
  433.                                 
  434.                                 Q3Set_Get(set, kElementTypeUpVector, &upVector);
  435.                                 
  436.                                 alreadyGotAttributes = kQ3True;
  437.                                 
  438.                                 if( groupSet )
  439.                                     Q3Set_Add(groupSet, kElementTypeUpVector, &upVector);
  440.                             }
  441.                             
  442.                             if( Q3Set_Contains( set, kElementTypeForwardDirection) == kQ3True ) {
  443.                                 TQ3Vector3D    forwardVector;
  444.                                 
  445.                                 Q3Set_Get(set, kElementTypeForwardDirection, &forwardVector);
  446.                                 
  447.                                 alreadyGotAttributes = kQ3True;
  448.                                 
  449.                                 if( groupSet )
  450.                                     Q3Set_Add(groupSet, kElementTypeForwardDirection, &forwardVector);
  451.                             }
  452.                             
  453.                             Q3Object_Dispose(set);
  454.                             
  455.                             // balances both the get or the new
  456.                             if( groupSet )
  457.                                 Q3Object_Dispose(groupSet);
  458.                         }
  459.                     }            
  460.                     
  461.                     // Note that since myGroup is a display group, we preserve the order of the objects in the file
  462.                     Q3Group_AddObject( myGroup, myTempObj ) ;
  463.                 }
  464.                 
  465.                 // we either added the object to the main group, or we don't care
  466.                 // so we can safely dispose of the object
  467.                 Q3Object_Dispose( myTempObj ) ;
  468.             }
  469.             
  470.             // check to see if we reached the end of file yet
  471.             isEOF = Q3File_IsEndOfFile( theFile );
  472.             
  473.         } while (isEOF == kQ3False);    
  474.     }
  475.     
  476.     if( alreadyGotAttributes != kQ3True ) {
  477.         StopAlert( NoScale_Alert, nil ) ;
  478.         return kQ3Failure;
  479.     }
  480.         
  481.     if( myGroup != NULL )
  482.         return kQ3Success ;
  483.     else
  484.         return kQ3Failure ;
  485. }
  486.  
  487. //-----------------------------------------------------------------------------------------------
  488. // cleaned up from IM QuickDraw 3D pp 15-5
  489. static TQ3FileObject MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText )
  490. {
  491.     TQ3FileObject        myFileObj;
  492.     TQ3StorageObject        myStorageObj;
  493.     OSType                myFileType;
  494.     
  495.     FInfo                fndrInfo ;
  496.  
  497.     // we assume the FSSpec passed in was valid, get the file information
  498.     // we need to know the file type, this routine may get called by an appleEvent
  499.     // handler, so we can't assume a type, we need to get it from the fsspec.
  500.     
  501.     FSpGetFInfo( myFSSpec, &fndrInfo ) ;
  502.     
  503.     // pull out the file type
  504.     
  505.     myFileType = fndrInfo.fdType ;
  506.     
  507.     // Create new storage object and new file object 
  508.     if(((myStorageObj = Q3FSSpecStorage_New( myFSSpec )) == NULL) 
  509.         || ((myFileObj = Q3File_New()) == NULL)) 
  510.     {
  511.         if (myStorageObj != NULL) 
  512.             Q3Object_Dispose(myStorageObj);
  513.         return(NULL);
  514.     }
  515.  
  516.     // Set the storage for the file object
  517.     Q3File_SetStorage(myFileObj, myStorageObj);
  518.     Q3Object_Dispose(myStorageObj);
  519.  
  520.     if (myFileType == '3DMF')
  521.         *isText = kQ3False ;
  522.     else if (myFileType == 'TEXT')
  523.         *isText = kQ3True ;
  524.  
  525.     return (myFileObj);
  526. }
  527.  
  528.  
  529. //-------------------------------------------------------------------------------------------
  530. //
  531. Boolean MetafileFileSpecify( FSSpec *theFile )
  532. {
  533.     StandardFileReply    theSFReply ;
  534.     SFTypeList            myTypes = { '3DMF' } ;
  535.     const short            numTypes = 1 ;
  536.         
  537.     // Get the file name to open
  538.     StandardGetFile( nil, numTypes, myTypes, &theSFReply ) ;
  539.     
  540.     if( theSFReply.sfGood )
  541.         *theFile = theSFReply.sfFile ;
  542.     
  543.     // did the user cancel?
  544.     return theSFReply.sfGood ;
  545.     
  546. }
  547. //----------------------------------------------------------------------------------
  548.  
  549.  
  550. void GetGroupBBox(
  551.     DocumentPtr            theDocument,
  552.     TQ3BoundingBox         *viewBBox)
  553. {
  554.     TQ3Point3D                     from     = { 0.0, 0.0, 1.0 };
  555.     TQ3Point3D                     to         = { 0.0, 0.0, 0.0 };
  556.     TQ3Vector3D                 up         = { 0.0, 1.0, 0.0 };
  557.     
  558.     float                         fieldOfView = .52359333333;
  559.     float                         hither         =  0.5;
  560.     float                         yon         =  1.5;
  561.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  562.  
  563.     TQ3Status                    status;
  564.     
  565.     status = GetDocumentGroupBoundingBox( theDocument , viewBBox) ;
  566.                                         
  567.     //
  568.     //  If we have a point model, then the "viewBBox" would end up
  569.     //  being a "singularity" at the location of the point.  As
  570.     //  this bounding "box" is used in setting up the camera spec,
  571.     //  we get bogus input into Escher.
  572.     
  573.     {
  574.          float        xSize, ySize, zSize;
  575.         
  576.         xSize = viewBBox->max.x - viewBBox->min.x;
  577.         ySize = viewBBox->max.y - viewBBox->min.y;
  578.         zSize = viewBBox->max.z - viewBBox->min.z;
  579.  
  580.         if (xSize <= kQ3RealZero &&
  581.             ySize <= kQ3RealZero &&
  582.             zSize <= kQ3RealZero) {
  583.             
  584.             viewBBox->max.x += 0.0001;
  585.             viewBBox->max.y += 0.0001;
  586.             viewBBox->max.z += 0.0001;
  587.             
  588.             viewBBox->min.x -= 0.0001;
  589.             viewBBox->min.y -= 0.0001;
  590.             viewBBox->min.z -= 0.0001;
  591.         }
  592.     }
  593. }
  594.  
  595.  
  596.  
  597.  
  598. //------------------------------------------------------------------------
  599.  
  600.  
  601. void AdjustCamera(
  602.     DocumentPtr            theDocument,
  603.     short                winWidth,
  604.     short                winHeight)
  605. {
  606.     TQ3BoundingBox                 viewBBox;
  607.     TQ3CameraObject                camera;
  608.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  609.     TQ3ViewObject                theView = theDocument->fView;
  610.     TQ3Vector3D                    objectFront;
  611.     
  612.     Q3View_GetCamera( theView, &camera);
  613.     GetGroupBBox( theDocument, &viewBBox);
  614.  
  615.     if( viewBBox.isEmpty == kQ3False ) {
  616.         float                                width,
  617.                                             length,
  618.                                             height,
  619.                                             largestDimension;
  620.                                             
  621.         TQ3TransformObject                    scaleTransform = NULL,
  622.                                             orientationTransform = NULL,
  623.                                             centerTransform = NULL;
  624.                                             
  625.         TQ3SetObject                        set;
  626.             
  627.         double                                scale;
  628.         TQ3Vector3D                            scaleTransformData={1.0, 1.0, 1.0};
  629.         TQ3RotateAboutAxisTransformData        rotateTransform={{0,0,0},{0,0,1},0};
  630.         TQ3Matrix4x4                        orientationMatrix = {    1.0, 0.0, 0.0, 0.0,
  631.                                                                     0.0, 1.0, 0.0, 0.0,
  632.                                                                     0.0, 0.0, 1.0, 0.0,
  633.                                                                     0.0, 0.0, 0.0, 1.0};
  634.         /*
  635.             The ordered group, theDocument->fModel, is going to contain the following transforms
  636.             
  637.             first a scale transform to take care of the scale attribute
  638.             second a matrix with the rotations to take care of the up and forward vectors info
  639.             third a translate transform to position the object starting at 0 in z and centered in x and y
  640.             
  641.             remember that the transforms are applied in the reverse order, which is to say that the
  642.             resulting point p' from point p is
  643.             
  644.             p' = scale * rotation * translate * p
  645.         */
  646.         
  647.         // initialize the scale and orientation transforms
  648.         
  649.         // add an identify transform for the scale, values will be set later if needed
  650.         scaleTransform = Q3ScaleTransform_New(&scaleTransformData);
  651.         if( scaleTransform )
  652.             Q3Group_AddObject(mainGroup, scaleTransform);
  653.  
  654.         // calculate the extents of the bounding box
  655.         width = viewBBox.max.x - viewBBox.min.x;
  656.         height = viewBBox.max.y - viewBBox.min.y;
  657.         length = viewBBox.max.z - viewBBox.min.z;
  658.  
  659.         // add an identify transform for the orientation, values will be set later if needed
  660.         
  661.         // the object's center will be set at 0,0,-length/2 later
  662.         rotateTransform.origin.x = 0.0;
  663.         rotateTransform.origin.y = 0.0;
  664.         rotateTransform.origin.z = - length * 0.5;
  665.  
  666.         orientationTransform = Q3MatrixTransform_New(&orientationMatrix);
  667.         if( orientationTransform )
  668.             Q3Group_AddObject(mainGroup, orientationTransform);
  669.         
  670.         // Now we get the custom elements for this model
  671.         Q3Shape_GetSet(mainGroup, &set);
  672.                             
  673.         if( set ) {
  674.             // let's get the scale
  675.             if( Q3Set_Contains( set, kElementTypeScale) == kQ3True ) {
  676.                 Q3Set_Get(set, kElementTypeScale, &scale);
  677.             }
  678.  
  679.             // let's get the up vector
  680.             if( Q3Set_Contains( set, kElementTypeUpVector) == kQ3True ) {
  681.                 TQ3Vector3D            cameraUpVector = {0.0, 1.0, 0.0},
  682.                                     upVector;
  683.                 TQ3TransformObject    tempTransform;
  684.                 
  685.                 Q3Set_Get(set, kElementTypeUpVector, &upVector);
  686.                 
  687.                 Q3Vector3D_Normalize(&upVector, &upVector);
  688.                 
  689.                 // Get the axis for the rotation
  690.                 Q3Vector3D_Cross(&cameraUpVector, &upVector, &rotateTransform.orientation);
  691.                 // Check to see that the vectors are not co-linear
  692.                 // WARNING - the code is not dealing with opposing vectors (ie one vector point in
  693.                 // +X direction and the other in -X.  The length will be zero, and so will the dot
  694.                 // product.  You will have to generate an arbitrary axis for the 180° rotation.
  695.                 if( Q3Vector3D_Length( &rotateTransform.orientation ) != 0.0) {
  696.                     Q3Vector3D_Normalize(&rotateTransform.orientation, &rotateTransform.orientation);
  697.                     // Get the angle, only valid if vectors are unit length
  698.                     rotateTransform.radians = acos(Q3Vector3D_Dot(&cameraUpVector, &upVector));
  699.                     tempTransform = Q3RotateAboutAxisTransform_New(&rotateTransform);
  700.     
  701.                     Q3MatrixTransform_Set(orientationTransform,
  702.                         Q3Transform_GetMatrix(tempTransform, &orientationMatrix));
  703.                         
  704.                     Q3Object_Dispose(tempTransform);
  705.                 }
  706.             }
  707.  
  708.             // let's get the forward vector
  709.             if( Q3Set_Contains( set, kElementTypeForwardDirection) == kQ3True ) {
  710.                 TQ3Vector3D                        cameraViewVector = {0.0, 0.0, 1.0},
  711.                                                 forwardDirection;
  712.                 TQ3TransformObject                tempTransform;
  713.                 TQ3Matrix4x4                    matrixUp, matrixForward;
  714.                 
  715.                 Q3Set_Get(set, kElementTypeForwardDirection, &forwardDirection);
  716.                 
  717.                 Q3Vector3D_Normalize(&forwardDirection, &forwardDirection);
  718.  
  719.                 // Get the axis for the rotation
  720.                 Q3Vector3D_Cross(&cameraViewVector, &forwardDirection, &rotateTransform.orientation);
  721.                 // Check to see that the vectors are not co-linear
  722.                 // WARNING - the code is not dealing with opposing vectors (ie one vector point in
  723.                 // +X direction and the other in -X.  The length will be zero, and so will the dot
  724.                 // product.  You will have to generate an arbitrary axis for the 180° rotation.
  725.                 if( Q3Vector3D_Length( &rotateTransform.orientation ) != 0.0) {
  726.                     Q3Vector3D_Normalize(&rotateTransform.orientation, &rotateTransform.orientation);
  727.                     // Get the angle, only valid if vectors are unit length
  728.                     rotateTransform.radians = acos(Q3Vector3D_Dot(&cameraViewVector, &forwardDirection));
  729.                     tempTransform = Q3RotateAboutAxisTransform_New(&rotateTransform);
  730.                     
  731.                     Q3Transform_GetMatrix(tempTransform, &matrixForward);
  732.     
  733.                     // concatenate with previous rotation, getting it from the matrix
  734.                     Q3Transform_GetMatrix(orientationTransform, &matrixUp);
  735.                     
  736.                     Q3MatrixTransform_Set(orientationTransform,
  737.                         Q3Matrix4x4_Multiply(&matrixUp, &matrixForward, &orientationMatrix));
  738.                         
  739.                     Q3Object_Dispose(tempTransform);
  740.                 }
  741.             }
  742.             
  743.             Q3Object_Dispose(set);
  744.         }
  745.         
  746.         // Now we'll deal with the scale
  747.  
  748.         largestDimension = width;
  749.         if( largestDimension < height)
  750.             largestDimension = height;
  751.         if( largestDimension < length)
  752.             largestDimension = length;
  753.  
  754.         largestDimension *= scale;
  755.         
  756.         if(    largestDimension > theDocument->units ) {
  757.                 short    itemHit ;
  758.                 
  759.                 itemHit = StopAlert( LargeObject_Alert, nil ) ;
  760.                 
  761.                 if( itemHit == 1 ){
  762.                     MenuHandle    hMenu;
  763.                     hMenu = GetMenu(Settings_Menu);
  764.  
  765.                     CheckItem(hMenu, Scale1M, false);
  766.                     CheckItem(hMenu, Scale10M, false);
  767.                     CheckItem(hMenu, Scale100M, false);
  768.                     CheckItem(hMenu, Scale1KM, false);
  769.                     
  770.                     if( largestDimension <= 10.0 ) {
  771.                         // the working units become 10 meters
  772.                         // scale the object down by 10
  773.                         if( largestDimension < 10.0)
  774.                             scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.1;
  775.                         Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
  776.                         theDocument->units = 10.0;
  777.                         CheckItem(hMenu, Scale10M, true);
  778.                     } else if( largestDimension <= 100.0 ) {
  779.                         // the working units become 100 meters
  780.                         // scale the object down by 100
  781.                         if( largestDimension < 100.0)
  782.                             scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.01;
  783.                         Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
  784.                         theDocument->units = 100.0;
  785.                         CheckItem(hMenu, Scale100M, true);
  786.                     } else if( largestDimension <= 1000.0 ) {
  787.                         // the working units become 1000 meters
  788.                         // scale the object down by 1000
  789.                         if( largestDimension < 1000.0)
  790.                             scaleTransformData.x = scaleTransformData.y = scaleTransformData.z = 0.001;
  791.                         Q3ScaleTransform_Set(scaleTransform, &scaleTransformData);
  792.                         theDocument->units = 1000.0;
  793.                         CheckItem(hMenu, Scale1KM, true);
  794.                     } else {
  795.                         itemHit = StopAlert( TooLargeObject_Alert, nil ) ;
  796.                     }
  797.                 }                
  798.         }
  799.          
  800.         // add a translate transform to center the object in the scene
  801.  
  802.         // move the object so that the front is at the view plane (Z direction) and centered in x and y
  803.         objectFront.x = - (viewBBox.min.x + width * 0.5);
  804.         objectFront.y = - (viewBBox.min.y + height * 0.5);
  805.         // We are looking into negative z, that's why we use maximum
  806.         objectFront.z = - viewBBox.max.z;
  807.         
  808.         centerTransform = Q3TranslateTransform_New(&objectFront);
  809.         Q3Group_AddObject(mainGroup, centerTransform);
  810.         Q3Object_Dispose(centerTransform);
  811.         
  812.         if( scaleTransform )
  813.             Q3Object_Dispose(scaleTransform);
  814.     }
  815.  
  816.     Q3ViewAngleAspectCamera_SetAspectRatio(
  817.         camera, (float) winWidth / (float) winHeight);
  818.     
  819.     Q3Object_Dispose(camera);
  820.     
  821.     return;
  822. }
  823.  
  824. // We use this function to do a quick check for attributes, to help us decide whether it's enough to look at 
  825. // the geometry's attribute set level, or if we need to go up to the shape's set
  826.  
  827. static TQ3Boolean CheckSetForAttributes(TQ3SetObject set) 
  828. {
  829.     if( Q3Set_Contains( set, kElementTypeName) == kQ3True )
  830.         return kQ3True;
  831.         
  832.     if( Q3Set_Contains( set, kElementTypeW3Anchor) == kQ3True )
  833.         return kQ3True;
  834.         
  835.     return kQ3False;
  836. }
  837.  
  838. // This function gets the attributes, if they exists, and display them
  839. static TQ3Boolean FindAndDisplayCustomAttributes(DocumentPtr theDocument, TQ3Object object)
  840. {
  841.     TQ3SetObject        set = NULL;
  842.     RGBColor            color = {0,0,0};
  843.  
  844.     RGBForeColor(&color);
  845.     TextMode(srcXor);
  846.     
  847.     // First we try at the Geometry level, if the object is a geometry
  848.     if( Q3Object_IsType(object, kQ3ShapeTypeGeometry) == kQ3True) {
  849.         Q3Geometry_GetAttributeSet(object, &set);
  850.         if( set && CheckSetForAttributes(set) == kQ3False ) {
  851.             /* Let's check at the shape set level */
  852.             Q3Object_Dispose(set);
  853.             set = NULL;
  854.         }
  855.     }
  856.     
  857.     // if we didn't get a set object, check at the Shape class level
  858.     if(set == NULL && Q3Object_IsType(object, kQ3SharedTypeShape) == kQ3True){
  859.         Q3Shape_GetSet(object, &set);
  860.     }
  861.         
  862.     if( set ) {
  863.         if( Q3Set_Contains( set, kElementTypeName) == kQ3True ) {
  864.             Str255            pascalString;
  865.             TQ3StringObject    string;
  866.             
  867.             Q3Set_Get( set, kElementTypeName, &string);
  868.  
  869.             if( theDocument->fStringObject != string ) {
  870.                 /* Delete previously displayed text */
  871.                 if( theDocument->fStringObject ) {
  872.                     Str255            pascalString;
  873.  
  874.                     strcpy((char *) pascalString, theDocument->fName);
  875.                     MoveTo(10,10);
  876.                     DrawString(pascalString);
  877.                     
  878.                     Q3CString_EmptyData(&theDocument->fName);
  879.                     Q3Object_Dispose(theDocument->fStringObject);
  880.                 }
  881.                 
  882.                 theDocument->fStringObject = string;
  883.                 
  884.                 Q3CString_GetString(theDocument->fStringObject, &theDocument->fName);
  885.                 
  886.                 strcpy((char *) pascalString, theDocument->fName);
  887.                 MoveTo(10,10);
  888.                 DrawString(c2pstr((char *)pascalString));
  889.             }
  890.         }
  891.  
  892.         if( Q3Set_Contains( set, kElementTypeW3Anchor) == kQ3True ) {
  893.             Str255                pascalString;
  894.             W3AnchorData    urlData;
  895.             
  896.             Q3Set_Get( set, kElementTypeW3Anchor, &urlData);
  897.  
  898.             if( theDocument->fDescription != urlData.description) {
  899.                 if( theDocument->fDescription ) {
  900.                     /* Delete previously displayed url */
  901.                     MoveTo(10,30);
  902.                     DrawString(theDocument->fURL);
  903.     
  904.                     MoveTo(10,50);
  905.                     strcpy((char *) pascalString, theDocument->fDescString);
  906.                     DrawString(c2pstr((char *)pascalString));
  907.                     
  908.                     Q3CString_EmptyData(&theDocument->fDescString);
  909.                     
  910.                     Q3Object_Dispose(theDocument->fDescription);
  911.                 }
  912.     
  913.                 MoveTo(10,30);
  914.                 strcpy((char *) theDocument->fURL, urlData.url);
  915.                 c2pstr((char *)theDocument->fURL);            
  916.                 DrawString(theDocument->fURL);
  917.     
  918.                 MoveTo(10,50);
  919.                 Q3CString_GetString(urlData.description, &theDocument->fDescString);
  920.                 strcpy((char *) pascalString, theDocument->fDescString);
  921.                 DrawString(c2pstr((char *)pascalString));
  922.                 
  923.                 theDocument->fDescription = urlData.description;
  924.             }
  925.             
  926.             if( theDocument->launchNetscape == kQ3True ) {
  927.                 if( OpenURL(urlData.url) == false)
  928.                     StopAlert( Netscape_Alert, nil );
  929.             }
  930.         }
  931.         
  932.         Q3Object_Dispose(set);
  933.         return( kQ3True );
  934.     }             
  935.     return( kQ3False );
  936. }
  937.  
  938. void DisplayCustomAttributes(DocumentPtr theDocument)
  939. {
  940.     TQ3HitData    hitData;
  941.     
  942.     Q3Pick_GetHitData(theDocument->fPickObject, 0, &hitData);
  943.     
  944.     if( hitData.object ) {
  945.         FindAndDisplayCustomAttributes(theDocument, hitData.object);
  946.     }
  947.     Q3Pick_EmptyHitList(theDocument->fPickObject);
  948.     Q3Hit_EmptyData(&hitData);
  949. }
  950.  
  951.